package com.hjh.netty.elect.demo4;

import cn.hutool.core.util.StrUtil;
import com.hjh.netty.elect.demo4.modbus.ModbusData;
import com.hjh.netty.elect.demo4.modbus.ModbusPayLoadDatas;
import com.hjh.netty.elect.demo4.modbus.ModbusResponse0x03;
import com.hjh.netty.elect.demo4.modbus.ModbusResponse0x10;
import com.hjh.netty.elect.util.CrcUtils;
import com.hjh.netty.elect.util.NumberDataConvertUtils;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Iot350协议解析实现
 * @author pyz
 *
 */
@Slf4j
@Service("iot350DataParseService")
public class Iot350DataParseServiceImpl implements Iot350DataParseService{

	//常用数据 (子类=0x01，版本=0)
	@Autowired
	private AnalysisUpLoadDataService analysisCommonlyUpLoadDataService;

	//实时测量数据 (子类=0x01，版本=1)
	@Autowired
	private AnalysisUpLoadDataService analysisRealtimeUpLoadDataService;

	//自定义测点数据 解析(子类=0x01，版本=2)
	@Autowired
	private AnalysisUpLoadDataService analysisCustomizeUpLoadDataService;

	//基本电能数据 (子类=0x02，版本=0)
	@Autowired
	private AnalysisUpLoadDataService analysisBasicEnergyUpLoadDataService;

	//基本谐波及电能质量数据 (子类=0x03，版本=0)
	@Autowired
	private AnalysisUpLoadDataService analysisBaseAndPowerQualityUpLoadDataService;

	//需量数据解析(子类=0x04，版本=0)
	@Autowired
	private AnalysisUpLoadDataService analysisDemandUpLoadDataService;

	/**
	 * 带解码器的解析
	 */
	@Override
	public BaseAutoUploadDatas<?> analysisPmc350DataByDecoder(ChannelHandlerContext ctx, Pmc350ProtocolModel protocolModel) throws Exception{
		//包标识
		String packetFlagAll = protocolModel.getPacketFlag();
		//解析过后包标识
		String packetFlag = convertPacketStr2PacketFlag(packetFlagAll);
		//示终端发送给主站的 Modbus 回复帧
		if("001".equals(packetFlag)) {
			return analysisModbusResponseData(protocolModel, packetFlagAll, packetFlag);
		}
		//自动上传数据
		else {
			return analysisAutoUploadData(protocolModel, packetFlagAll, packetFlag);

		}

	}

	/**
	 * 解析自动上传的数据
	 * @param protocolModel
	 * @param packetFlagAll
	 * @return
	 */
	public BaseAutoUploadDatas<PayLoadDatas> analysisAutoUploadData(Pmc350ProtocolModel protocolModel, String packetFlagAll, String packetFlag){
		BaseAutoUploadDatas<PayLoadDatas> uploadDatas = new BaseAutoUploadDatas<PayLoadDatas>();
		//包标识
		uploadDatas.setPacketFlagAll(packetFlagAll);
		//转换过后的包标识
		uploadDatas.setPacketFlag(packetFlag);
		//设备识别码长度
		uploadDatas.setDeviceIdCodeLength(protocolModel.getDeviceIdCodeLength()); //这里解析的时候已经加过1了
		//设备识别码(注册包)
		String diveceIdCode = protocolModel.getDeviceIdCode();
		uploadDatas.setDeviceIdCode(diveceIdCode);
		//PayLoad 部分总长度
		int payLoadLength = protocolModel.getPayLoadLength();
		uploadDatas.setPayLoadLength(payLoadLength);
		//PayLoad 校验和
		String payLoadVerifyTotal = protocolModel.getPayLoadVerifyTotal();
		uploadDatas.setPayLoadVerifyTotal(payLoadVerifyTotal);
		//如果PayLoad没有长度，应该是心跳包
		if(payLoadLength <= 0) {
			return uploadDatas;
		}
		//PayLoad部分
		String payLoadstr = protocolModel.getPayLoad();
		PayLoadDatas payLaodObj = analysisPayLoadData(payLoadstr);
		uploadDatas.setPayLaod(payLaodObj);
		return uploadDatas;
	}

	/**
	 * 解析modbus回复的消息
	 * @param protocolModel
	 * @param packetFlagAll
	 * @return
	 */
	public BaseAutoUploadDatas<ModbusPayLoadDatas> analysisModbusResponseData(Pmc350ProtocolModel protocolModel, String packetFlagAll, String packetFlag){
		BaseAutoUploadDatas<ModbusPayLoadDatas> uploadDatas = new BaseAutoUploadDatas<ModbusPayLoadDatas>();
		//包标识
		uploadDatas.setPacketFlagAll(packetFlagAll);
		//转换过后的包标识
		uploadDatas.setPacketFlag(packetFlag);
		//设备识别码长度
		uploadDatas.setDeviceIdCodeLength(protocolModel.getDeviceIdCodeLength()); //这里解析的时候已经加过1了
		//设备识别码(注册包)
		String diveceIdCode = protocolModel.getDeviceIdCode();
		uploadDatas.setDeviceIdCode(diveceIdCode);
		//PayLoad 部分总长度
		int payLoadLength = protocolModel.getPayLoadLength();
		uploadDatas.setPayLoadLength(payLoadLength);
		//PayLoad 校验和
		String payLoadVerifyTotal = protocolModel.getPayLoadVerifyTotal();
		uploadDatas.setPayLoadVerifyTotal(payLoadVerifyTotal);
		//如果PayLoad没有长度，应该是心跳包
		if(payLoadLength <= 0) {
			return uploadDatas;
		}
		//PayLoad部分
		String payLoadstr = protocolModel.getPayLoad();
		ModbusPayLoadDatas payLaodObj = analysisModbusPayLoadData(payLoadstr);
		uploadDatas.setPayLaod(payLaodObj);
		return uploadDatas;
	}

	/**
	 * 解析PayLoad数据
	 * @param payLoadStr
	 * @return
	 */
	public PayLoadDatas analysisPayLoadData(String payLoadStr) {
		if(StrUtil.isEmpty(payLoadStr)) {
			return null;
		}
		int index = 0;
		PayLoadDatas payLoad = new PayLoadDatas();
		//上送类型/子类型
		String mainAndSubType = payLoadStr.substring(index, index + 2);
		payLoad.setMainAndSubType(mainAndSubType);
		convertUpDataMainTypeAndUpDataSubType(payLoad, mainAndSubType);
		index += 2;
		//分组号及子类型版本号
		String upGrpNumAndDataTypeVer = payLoadStr.substring(index, index + 2);
		payLoad.setUpGrpNumAndDataTypeVer(upGrpNumAndDataTypeVer);
		convertUpGrpNumAndUpDataTypeVer(payLoad, upGrpNumAndDataTypeVer);
		index += 2;
		//数据包标识
		String packageId = payLoadStr.substring(index, index + 2);
		payLoad.setPackageId(packageId);
		index += 2;
		//子设备标识符
		String subDivece = payLoadStr.substring(index, index + 4);
		payLoad.setSubDivece(subDivece);
		index += 4;
		//上送时间
		//年
		String year = payLoadStr.substring(index, index + 2);
		payLoad.setYear("20" + NumberDataConvertUtils.hexString2decimalInt(year));
		index += 2;
		//月
		String month = payLoadStr.substring(index, index + 2);
		payLoad.setMonth(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(month)));
		index += 2;
		//日
		String day = payLoadStr.substring(index, index + 2);
		payLoad.setDay(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(day)));
		index += 2;
		//时
		String hour = payLoadStr.substring(index, index + 2);
		payLoad.setHour(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(hour)));
		index += 2;
		//分
		String minute = payLoadStr.substring(index, index + 2);
		payLoad.setMinute(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(minute)));
		index += 2;
		//秒
		String second = payLoadStr.substring(index, index + 2);
		payLoad.setSecond(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(second)));
		index += 2;
		//UpLoadData数据部分
		String upLoadData = payLoadStr.substring(index);
		Object reData = null;
		//定时上送数据
		if("001".equals(payLoad.getUpDataMainType())) {
			reData = analysisTimingUpLoadData(payLoad, upLoadData);
		}
		//事件数据上送
		else if("010".equals(payLoad.getUpDataMainType())) {
			reData = analysisEventUpLoadData(payLoad, upLoadData);
		}
		//记录数据上送
		else if("011".equals(payLoad.getUpDataMainType())) {

		}
		//封装返回
		payLoad.setUpLoadData(reData);
		return payLoad;
	}

	/**
	 * 解析modbus 回复的消息
	 * @param payLoadStr
	 * @return
	 */
	public ModbusPayLoadDatas analysisModbusPayLoadData(String payLoadStr) {
		if(StrUtil.isEmpty(payLoadStr)) {
			return null;
		}
		int index = 0;
		ModbusPayLoadDatas payLoad = new ModbusPayLoadDatas();
		//数据包标识
		String packageId = payLoadStr.substring(index, index + 2);
		index += 2;
		payLoad.setPackageId(packageId);
		//Mid号
		String midNo = payLoadStr.substring(index, index + 4);
		index += 4;
		payLoad.setMidNo(midNo);
		//回复帧类型
		String responseType = payLoadStr.substring(index, index + 2);
		index += 2;
		payLoad.setResponseType(responseType);
		//UpLoadData数据部分
		String modbusRtuData = payLoadStr.substring(index);
		ModbusData modbusResponse = analysisModbusRtuData(modbusRtuData);
		payLoad.setModbusDatas(modbusResponse);

		return payLoad;

	}

	/**
	 * 解析modbus返回的RTU消息
	 * @param modbusRtuData
	 * @return
	 */
	private ModbusData analysisModbusRtuData(String modbusRtuData) {
		ModbusData modbusData = new ModbusData();
		modbusData.setModbusRtuData(modbusRtuData);
		int index = 0;
		//从站地址（设备id），1字节
		String deviceId = modbusRtuData.substring(index, index + 2);
		modbusData.setDeviceId(deviceId);
		index += 2;
		//功能码，1字节，这里既是10
		String functionCode = modbusRtuData.substring(index, index + 2);
		modbusData.setFunctionCode(functionCode);
		index += 2;
		Object obj = null;
		//写寄存器（0x10）
		if("10".equals(functionCode)) {
			obj = analysisModbusRtuData0x10(deviceId, functionCode, modbusRtuData, index);

		}
		//读寄存器（0x03）
		else if("03".equals(functionCode)) {
			obj = analysisModbusRtuData0x03(deviceId, functionCode, modbusRtuData, index);
		}
		//其他的提示
		else {
			log.info("收到返回modbus的功能码不正确，功能码为：" + functionCode);

		}
		modbusData.setData(obj);

		return modbusData;
	}

	/**
	 * 解析解析modbus返回的RTU消息（0x10）
	 * @param deviceId 设备标识
	 * @param functionCode 功能码
	 * @param modbusRtuData 上传数据
	 * @param index 当前解析的下标
	 * @return
	 */
	private Object analysisModbusRtuData0x10(String deviceId, String functionCode, String modbusRtuData, int index) {
		ModbusResponse0x10 result = new ModbusResponse0x10();
		//寄存器起始,2字节
		String registerAddress = modbusRtuData.substring(index, index + 4);
		result.setRegisterAddress(registerAddress);
		index += 4;
		//寄存器数量,2字节
		String registerCount = modbusRtuData.substring(index, index + 4);
		result.setRegisterCount(registerCount);
		index += 4;
		//CRC校验码，2字节
		String crcCode = modbusRtuData.substring(index, index + 4);
		result.setCrcCode(crcCode);
		index += 4;
		//判断crc编码是否正确
		String newData = deviceId + functionCode + registerAddress + registerCount;
		String newCrcData = CrcUtils.getCRC(newData);
		if(!crcCode.equals(newCrcData)) {
			log.error("modbus RTU数据["+modbusRtuData+"]CRC校验不通过，不做处理");
			return null;
		}
		return result;
	}

	/**
	 * 解析解析modbus返回的RTU消息（0x03）
	 * @param deviceId 设备标识
	 * @param functionCode 功能码
	 * @param modbusRtuData 上传数据
	 * @param index 当前解析的下标
	 * @return
	 */
	private Object analysisModbusRtuData0x03(String deviceId, String functionCode, String modbusRtuData, int index) {
		ModbusResponse0x03 result = new ModbusResponse0x03();
		//字节数n,1字节
		String byteCountStr = modbusRtuData.substring(index, index + 2);
		int byteCount = NumberDataConvertUtils.hexString2decimalInt(byteCountStr);
		result.setByteCount(byteCount);
		index += 2;
		//上传数据,n字节
		String uploadData = modbusRtuData.substring(index, index + (byteCount * 2));
		result.setUploadData(uploadData);
		index = index + (byteCount * 2);
		//CRC校验码，2字节
		String crcCode = modbusRtuData.substring(index, index + 4);
		result.setCrcCode(crcCode);
		index += 4;
		//判断crc编码是否正确
		String newData = deviceId + functionCode + byteCountStr + uploadData;
		String newCrcData = CrcUtils.getCRC(newData);
		if(!crcCode.equals(newCrcData)) {
			log.error("modbus RTU数据["+modbusRtuData+"]CRC校验不通过，不做处理");
			return null;
		}
		return result;
	}

	/**
	 * 解析定时上送数据upLoadData
	 * @param payLoadDatas	解析的含子类型和版本部分的payLoad对象
	 * @param upLoadDataStr  upLoadData 16进制字符串
	 * @return 返回解析完成的upLoadData对象
	 */
	public Object analysisTimingUpLoadData(PayLoadDatas payLoadDatas, String upLoadDataStr) {
		//判断版本类型
		if(payLoadDatas.getUpDataSubType() == 1) {
			//常用数据 (子类=0x01，版本=0) 
			if(payLoadDatas.getUpDataTypeVer() == 0) {
				return analysisCommonlyUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
			//实时测量数据 (子类=0x01，版本=1)
			else if(payLoadDatas.getUpDataTypeVer() == 1) {
				return analysisRealtimeUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
			//自定义测点数据 (子类=0x01，版本=2)
			else if(payLoadDatas.getUpDataTypeVer() == 2) {
				return analysisCustomizeUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
		}
		else if(payLoadDatas.getUpDataSubType() == 2) {
			//基本电能数据 (子类=0x02，版本=0)
			if(payLoadDatas.getUpDataTypeVer() == 0) {
				return analysisBasicEnergyUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
			//分时电能数据 (子类=0x02，版本=1~4)
			else if(payLoadDatas.getUpDataTypeVer() == 1
					|| payLoadDatas.getUpDataTypeVer() == 2
					|| payLoadDatas.getUpDataTypeVer() == 3
					|| payLoadDatas.getUpDataTypeVer() == 4) {

			}

		}
		else if(payLoadDatas.getUpDataSubType() == 3) {
			//基本谐波及电能质量数据 (子类=0x03，版本=0)
			if(payLoadDatas.getUpDataTypeVer() == 0) {
				return analysisBaseAndPowerQualityUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
			//电流分次谐波数据 (子类=0x03，版本=1~3)
			else if(payLoadDatas.getUpDataTypeVer() == 1
					|| payLoadDatas.getUpDataTypeVer() == 2
					|| payLoadDatas.getUpDataTypeVer() == 3) {


			}
			//电压分次谐波数据 (子类=0x03，版本=4~6)
			else if(payLoadDatas.getUpDataTypeVer() == 4
					|| payLoadDatas.getUpDataTypeVer() == 5
					|| payLoadDatas.getUpDataTypeVer() == 6) {


			}
		}
		else if(payLoadDatas.getUpDataSubType() == 4) {
			//需量数据 (子类=0x04，版本=0)
			if(payLoadDatas.getUpDataTypeVer() == 0) {
				return analysisDemandUpLoadDataService.analysisUpLoadData(upLoadDataStr);
			}
		}
		return null;
	}

	/**
	 * 解析事件上送数据upLoadData
	 * @param payLoadDatas	解析的含子类型和版本部分的payLoad对象
	 * @param upLoadDataStr  upLoadData 16进制字符串
	 * @return 返回解析完成的upLoadData对象
	 */
	public Object analysisEventUpLoadData(PayLoadDatas payLoadDatas, String upLoadDataStr) {
		//payLoadDatas.setUpLoadData(upLoadDataStr);
		//log.info("事件上送：PayLoadDatas:{}, upLoadDataStr:{}", JSONObject.toJSONString(payLoadDatas), upLoadDataStr);
		int index = 0;
		SoeDatas soeDatas = new SoeDatas();
		soeDatas.setUploadSoeData(upLoadDataStr);
		//本次上送事件总条数,4个字节
		String upLoadSOENumStr = upLoadDataStr.substring(index, index + 8);
		int upLoadSOENum = NumberDataConvertUtils.hexString2decimalInt(upLoadSOENumStr);
		soeDatas.setUpLoadSOENum(upLoadSOENum);
		index += 8;
		//本次上送 SOE 起始指针,4个字节
		String upLoadSOEStartPtr = upLoadDataStr.substring(index, index + 8);
		soeDatas.setUpLoadSOEStartPtr(upLoadSOEStartPtr);
		index += 8;
		//上送的事件SOE log
		List<SoeLog> soeLogList = new ArrayList<>();
		for(int i = 0; i < upLoadSOENum; i++) {
			SoeLog soeLog = new SoeLog();
			//大类
			String higthTypeStr = upLoadDataStr.substring(index, index + 2);
			soeLog.setHigthType(NumberDataConvertUtils.hexString2decimalInt(higthTypeStr));
			index += 2;
			//子类
			String lowTypeStr = upLoadDataStr.substring(index, index + 2);
			soeLog.setLowType(NumberDataConvertUtils.hexString2decimalInt(lowTypeStr));
			index += 2;
			//年
			String year = upLoadDataStr.substring(index, index + 2);
			soeLog.setYear("20" + NumberDataConvertUtils.hexString2decimalInt(year));
			index += 2;
			//月
			String month = upLoadDataStr.substring(index, index + 2);
			soeLog.setMonth(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(month)));
			index += 2;
			//日
			String day = upLoadDataStr.substring(index, index + 2);
			soeLog.setDay(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(day)));
			index += 2;
			//时
			String hour = upLoadDataStr.substring(index, index + 2);
			soeLog.setHour(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(hour)));
			index += 2;
			//分
			String minute = upLoadDataStr.substring(index, index + 2);
			soeLog.setMinute(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(minute)));
			index += 2;
			//秒
			String second = upLoadDataStr.substring(index, index + 2);
			soeLog.setSecond(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(second)));
			index += 2;
			/*毫秒, Uint16, 2 字节*/
			String millisecond = upLoadDataStr.substring(index, index + 4);
			soeLog.setMillisecond(String.valueOf(NumberDataConvertUtils.hexString2decimalInt(millisecond)));
			index += 4;
			/*双位置信息, Uint16, 2 字节，（0：分/返回，1：合/动作/报警）*/
			String actionInfo = upLoadDataStr.substring(index, index + 4);
			soeLog.setActionInfo(actionInfo);
			index += 4;;
			/*记录值, Float/Uint32, 4 字节*/
			String recordInfo = upLoadDataStr.substring(index, index + 8);
			soeLog.setRecordInfo(recordInfo);
			index += 8;

			soeLogList.add(soeLog);
		}
		soeDatas.setSoeLogList(soeLogList);
		return soeDatas;
	}

	/**
	 * 包标识 转换：
	 * 主动上送：高 5 bit 为预留，固定0，低 3bit 为包标识
	 * 设备应答：高3 bit为包标识，其他为0
	 * @param packetStr
	 * @return
	 */
	private String convertPacketStr2PacketFlag(String packetStr) {
		//将16进制转为二进制
		String binaryString = NumberDataConvertUtils.hexString2binaryString(packetStr);
		return binaryString.substring(5);
	}

	/**
	 * 转换设备识别码长度
	 * bit 0~3设备识别码，Bit 4~7：预留
	 * 设备识别码(即注册包)长度 RegLen -1，RegLen∈[1，16]
	 * 0 代表设备识别码长度为 1
	 * 1代表设备识别码长度为 2
	 * ……
	 * 15 代表设备识别码长度为 16
	 * @param DiveceIdCode
	 * @return
	 */
//	private int convertDiveceIdCodeLength(String diveceIdCode) {
//		//将16进制转为二进制
//		String binaryString = NumberDataConvertUtils.hexString2binaryString(diveceIdCode);
//		String decimalString = NumberDataConvertUtils.binaryStrings2decimalString(binaryString.substring(4));
//		return Integer.parseInt(decimalString) + 1; //这里要+1
//	}

	/**
	 * 转换上送类型和子类型
	 * bit 7~5：表示上送类型，001：定时数据上送，010：事件数据上送，011：记录数据上送，其他：预留
	 * bit 4~0： 表示上送子类型 upDataSubType
	 * @param autoUploadData 上送报文对象
	 * @param mainAndSubType 上送类型/子类型的16进制
	 * @return
	 */
	private void convertUpDataMainTypeAndUpDataSubType(PayLoadDatas autoUploadData, String mainAndSubType) {
		//将16进制转为二进制
		String binaryString = NumberDataConvertUtils.hexString2binaryString(mainAndSubType);
		String mainType = binaryString.substring(0, 3);
		String subType = binaryString.substring(3);
		//mainType不转进制
		autoUploadData.setUpDataMainType(mainType);
		//二进制转十进制
		autoUploadData.setUpDataSubType(Integer.parseInt(NumberDataConvertUtils.binaryStrings2decimalString(subType)));
	}
	/**
	 * 转换分组号及子类型版本号
	 * bit 7~4 ：分组号 upGrpNum
	 * bit 3~0 ：版本号 upDataTypeVer
	 * @param autoUploadData 上送报文PayLoad对象
	 * @param upGrpNumAndDataTypeVer 分组号及子类型版本号的16进制
	 */
	private void convertUpGrpNumAndUpDataTypeVer(PayLoadDatas autoUploadData, String upGrpNumAndDataTypeVer) {
		//将16进制转为二进制
		String binaryString = NumberDataConvertUtils.hexString2binaryString(upGrpNumAndDataTypeVer);
		String upGrpNum = binaryString.substring(0, 4);
		String upDataTypeVer = binaryString.substring(4);
		//二进制转十进制
		autoUploadData.setUpGrpNum(Integer.parseInt(NumberDataConvertUtils.binaryStrings2decimalString(upGrpNum)));
		autoUploadData.setUpDataTypeVer(Integer.parseInt(NumberDataConvertUtils.binaryStrings2decimalString(upDataTypeVer)));
	}

}
